<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title>js10.0 深入理解this</title>
	</head>
	<body>
		<script type="text/javascript">
			
			// 用于演示函数中的this在对函数不同的调用方式中指向不同的函数
			function foo() {
				console.log(this.a);
			}
			
			/*默认绑定：没有其他绑定规则存在时的默认规则。这也是函数调用中最常用的规则。*/
			
			
			var a = 2;
			// 因为foo()是直接调用的（独立函数调用），没有应用其他的绑定规则，这里进行了默认绑定，
			// 将全局对象绑定this上，所以this.a 就解析成了全局变量中的a，即2
			foo();  // 2
			/*默认绑定演示结束*/
			
			/*隐式绑定：*/
			var a = 2;
			
			var obj = { 
			    a: 3,
			    foo: foo 
			};
			
			// 这里foo函数被当做引用属性，被添加到obj对象上。这里的调用过程是这样的 
			// 获取obj.foo属性 -> 根据引用关系找到foo函数，执行调用
			// 所以这里对foo的调用存在上下文对象obj，this进行了隐式绑定，即this绑定到了obj上，所以this.a被解析成了obj.a，
			obj.foo(); // 3
			/*隐式绑定结束*/
			
			/*多层调用链*/
			var a = 2;
			
			var obj1 = { 
			    a: 4,
			    foo: foo 
			};
			
			var obj2 = { 
			    a: 3,
			    obj1: obj1
			};
			// 函数的调用过程：
            // 先获取obj1.obj2 -> 通过引用获取到obj2对象，再访问 obj2.foo -> 最后执行foo函数调用
			// 这里调用链不只一层，存在obj1、obj2两个对象，这里原则是获取最后一层调用的上下文对象，即obj2，所以结果显然是4（obj2.a）
			obj2.obj1.foo(); // 4
			/* 多层调用链结束 */
			
			
			/* 隐式丢失（函数别名）*/
			var a = 2;
			
			var obj = { 
			    a: 3,
			    foo: foo 
			};
			// 赋值给bar的实际上就是foo函数（即：bar指向foo本身）。
            // 实际的调用关系是：通过bar找到foo函数，进行调用。整个调用过程并没有obj的参数，所以是默认绑定，全局属性a。结果为2
			var bar = obj.foo;
			bar(); // 2
			
			/* 隐式丢失(函数别名)结束 */
			
			
			/* 隐式丢失（回调函数）*/
			var a = 2;
			
			var obj = { 
			    a: 3,
			    foo: foo 
			};
			
			// 虽然参传是obj.foo，但传参实际上传的就是foo函数对象本身的引用。
			// 对于setTimeout的调用，还是 setTimeout -> 获取参数中foo的引用参数 -> 执行 foo 函数，中间没有obj的参与。这里依旧进行的是默认绑定
		    setTimeout(obj.foo, 100); // 2
			
			/* 隐式丢失（回调函数）结束*/
			
			
			/* 显式绑定*/
			var a = 2;
			
			var obj1 = { 
			    a: 3,
			};
			
			var obj2 = { 
			    a: 4,
			};
			// 这里因为显示的申明了要绑定的对象，所以this就被绑定到了obj上，打印的结果自然就是obj1.a 和obj2.a
			console.log("显式绑定1结果")
			foo.call( obj1 ); // 3
			console.log("显式绑定2结果")
			foo.call( obj2 ); // 4
			/* 显式绑定结束 */
			
			
			/* 显式绑定（硬绑定）*/
			var a = 2;
			
			var obj1 = { 
			    a: 3,
			};
			
			var obj2 = { 
			    a: 4,
			};
			
			var bar = function(){
			    foo.call( obj1 );
			}
			
			setTimeout( bar, 100 ); // 3
			
			// 虽然bar被显示绑定到obj2上，对于bar，function(){…} 中的this确实被绑定到了obj2，
			// 而foo因为通过foo.call( obj1 )已经显示绑定了obj1，所以在foo函数内，this指向的是obj1，
			// 不会因为bar函数内指向obj2而改变自身。所以打印的是obj1.a（即3）。
			bar.call( obj2 ); //  3
			/* 显式绑定（硬绑定）结束 */
			
			
			/* new 绑定 */
			
			function foo(a){
				this.a = a;
			}
			var a = 2;
			
			var bar1 = new foo(3);
			console.log(bar1.a); // 3
			
			var bar2 = new foo(4);
			console.log(bar2.a); // 4
			
			// 因为每次调用生成的是全新的对象，该对象又会自动绑定到this上，所以答案显而易见。
			/* new 绑定结束 */
			
			
			// 显式绑定时对于null,undefined的绑定不会生效。会默认使用全局变量
			foo.call( null ); // 2
			foo.call( undefined ); // 2
		</script>
	</body>
</html>
